﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Xml.Serialization;

using System.Web;

namespace AnOtherBlog.Core
{
	/// <summary>
	/// Mantiene la configuración del Blog
	/// </summary>
	/// <remarks>
	/// Me parece que esta clase tambien tiene que derivar de BlogBaseClass
	/// de ese modo se pueden implementar los mismos mecanismos de control
	/// que para los otros objetos de la aplicación REVISAR !!!!!!!! 
	/// </remarks>
	public class Blog
	{
		#region Constants
		/// <summary>
		/// Esta constante es para acceder al objeto Dictionary Application
		/// donde vamos a mantener la configuración de los blog que están 
		/// activos en la aplicación
		/// </summary>
		public const string _ANOTHERBLOGDOMAINS = "_ANOTHERBLOGDOMAINS";

		#endregion

		#region Constructor
		/// <summary>
		/// Constructor por defecto
		/// </summary>
		public Blog()
		{
			//this.BlogSettings = new BlogSetting();
		}

		#endregion

		#region Fields and Properties for application and session access

		/// <summary>
		/// Diccionario que mantiene instancias de blog por cada host (dominio o subdominio)
		/// </summary>
		[XmlIgnore()]
		public static System.Collections.Generic.Dictionary<string, Blog> AnOtherBlogDomains
		{
			get
			{
				if (HttpContext.Current.Application[_ANOTHERBLOGDOMAINS] == null)
				{
					HttpContext.Current.Application[_ANOTHERBLOGDOMAINS] = new System.Collections.Generic.Dictionary<string, Blog>();
				}
				return (System.Collections.Generic.Dictionary<string, Blog>) HttpContext.Current.Application[_ANOTHERBLOGDOMAINS];
			}
		}

		/// <summary>
		/// Referencia a la instancia actual del blog
		/// </summary>
		[XmlIgnore()]
		public static Blog Current
		{
			// TODO: Revisar la arquitectura de la aplicación.

			// La idea es que se tenga varios dominios apuntando a un mismo
			// hosting, también pueden ser subdominios, por ejemplo
			// http://jose.midominio.com.ar y otro http://pedro.midominio.com.ar
			// en este caso se deben levantar las configuraciones adecuadas,
			// ambos blogs existen en la base de datos pero con diferentes ids
			// (Bastante osada la idea, pero puede funcionar)

			// Esta funcionando !!!!!!!!!!!!!!
			get
			{
				string domain = HttpContext.Current.Request.Url.Host;

				if (!Blog.AnOtherBlogDomains.ContainsKey(domain))
				{
					Blog.AnOtherBlogDomains.Add(domain, Load(domain));
				}
				return Blog.AnOtherBlogDomains[domain];
			}
		}

		#endregion

		#region Fields and Properties

		private int _BlogID = 1;
		/// <summary>
		/// Identificador único del blog (en esta base de datos)
		/// </summary>
		public int BlogID
		{
			get
			{
				return _BlogID;
			}
			set
			{
				_BlogID = value;
			}
		}

		private string _BlogDomain = string.Empty;
		/// <summary>
		/// Dominio (URL) del blog.
		/// </summary>
		public string BlogDomain
		{
			get
			{
				return _BlogDomain;
			}
			set
			{
				_BlogDomain = value ?? string.Empty;
			}
		}

		private BlogSetting _BlogSettings;
		/// <summary>
		/// Settings, opciones del blog
		/// </summary>
		public BlogSetting BlogSettings
		{
			get
			{
				if (_BlogSettings == null)
				{
					_BlogSettings = new BlogSetting();
				}
				return _BlogSettings;
			}
			set
			{
				_BlogSettings = value;
			}
		}

		private List<Category> _Categories;
		/// <summary>
		/// Colección de todas las categorías del blog
		/// </summary>
		/// <remarks>
		/// Hay que revisar esta cuestión de serializar o no.
		/// En una situación de mayor escala cuando se almacena
		/// el estado de la aplicación y session en otro servidor
		/// o en una base de datos seguramente va a necesitar que
		/// sea serializable
		/// </remarks>
		// TODO: Estudiar sobre como se almacena el estado de la aplicacion y sesion !!!!!!!!!!!!!!!!!
		[XmlIgnore()]
		public List<Category> Categories
		{
			get
			{
				if (_Categories == null || _Categories.Count == 0)
				{
					_Categories = Category.LoadAll();
				}
				return _Categories;
			}
		}

		private List<Post> _Posts;
		/// <summary>
		/// Colección de todas las publicaciones del blog en modo Proxy
		/// </summary>
		[XmlIgnore()]
		public List<Post> Posts
		{
			get
			{
				if (_Posts == null || _Posts.Count == 0)
				{
					// Carga la colección de publicaciones en modo de lectura reducida (IsProxy=true)
					_Posts = Post.LoadAll(Post.PostSort.DateCreated, true);
				}
				return _Posts;
			}
		}
		
		#endregion


		/// <summary>
		/// Inicialización de las propiedades del blog
		/// 
		/// Necesario para la primera vez que funciona 
		/// </summary>
		/// <param name="id">Identificador del blog</param>
		/// <returns></returns>
		private static Blog BlogInitialize()
		{
			Blog blog = new Blog();

			// Not Serialized Properties

			// Serialized Properties
			blog.BlogID = 0;
			blog.BlogDomain = "";

			// Settings Properties
			blog.BlogSettings.Name = "Nombre del Blog";
			blog.BlogSettings.Description = "Descripción del Blog";
			
			return blog;
		}

		/// <summary>
		/// Resetea el diccionario
		/// otra forma de hacerlo es con un touch al web.config o global.asax
		/// </summary>
		public static void BlogReSet()
		{
			HttpContext.Current.Application.Lock();

			foreach(string domain in Blog.AnOtherBlogDomains.Keys)
			{
				Blog blog = Blog.AnOtherBlogDomains[domain];
				//Blog.AnOtherBlogDomains[domain] = null;

				blog.Categories.Clear();
				blog.Posts.Clear();
				// TODO: esto no me gusta, hay que tener en mente algo automático
			}
			Blog.AnOtherBlogDomains.Clear();

			HttpContext.Current.Application.UnLock();

		}

		#region Persist Methods

		/// <summary>
		/// Carga la configuración del blog por su dominio
		/// </summary>
		/// <param name="domain">Dominio del blog</param>
		/// <returns>Objeto blog</returns>
		public static Blog Load(string domain)
		{
			Blog blog = Providers.DataAccess.BlogSelect(domain);
			if (blog == null)
			{
				blog = BlogInitialize();

				// TODO: En el proveedor de datos se genera un ID para el blog. Revisar esta decisión
				blog.BlogID = 0;
				blog.BlogDomain = domain;

				Providers.DataAccess.BlogInsert(blog);
			}
			return blog;
		}

		/// <summary>
		/// Guarda la configuración del blog
		/// </summary>
		public void Save()
		{
			Providers.DataAccess.BlogUpdate(this);
		}

		#endregion

		#region Reload Methods
		/// <summary>
		/// Recarcarga la lista de categorías del blog
		/// </summary>
		public void ReLoadCategories()
		{
			if (_Categories != null)
			{
				// No se si este método libera la memoria inmediatamente o necesita del Garbage Collector???
				// ya vamos a averiguar bien esta cuestión
				_Categories.Clear();
			}
			//_Categories = Category.LoadAll();
		}

		/// <summary>
		/// Recarcarga la lista de publicaciones del blog
		/// </summary>
		public void ReLoadPosts()
		{
			if (_Posts != null)
			{
				// No se si este método libera la memoria inmediatamente o necesita del Garbage Collector???
				// ya vamos a averiguar bien esta cuestión
				_Posts.Clear();
			}
		}

		#endregion

		/// <summary>
		/// Determina si existe una categoría de acuerdo a su nombre
		/// </summary>
		/// <param name="categoryName">Nombre de la categoría</param>
		/// <returns>True si la categoría existe en la colección</returns>
		public bool CategoryExists(string categoryName)
		{
			return Blog.Current.Categories.Exists(delegate(Category c)
			{
				return (string.Compare(c.CategoryName, categoryName, true) == 0);
			});
		}

		/// <summary>
		/// Busca una categoría de acuerdo a su identificador
		/// </summary>
		/// <param name="categoryID">Identificador único de la categoría</param>
		/// <returns>si existe devuelve el objeto sino devuelve el objeto default???</returns>
		public Category CategoryFind(Guid categoryID)
		{
			return Blog.Current.Categories.Find(delegate(Category c)			
			{
				return c.CategoryID == categoryID;
			});
		}

	}
}
